/*
 *  ifxcursor.cpp - Informix dbExpress cursor implementation
 *
 *    Copyright (c) 2001-2006, Luxena Software Company. All rights reserved.
 *
 *  Purpose:
 *
 */

#include "ifxcursor.hpp"

#include <new>
#include <stdio.h>
#include <ifxtypes.h>
#include <sqltypes.h>
#include <sqliapi.h>
#include "ifxdefs.hpp"
#include "ifxerror.hpp"


/////////////////////////////////////////////////////////////////////////////
// CColumnDescription

void CColumnDescription::describe (pCHAR name, DataType dataType, UINT32 length,
	INT16 precision, INT16 scale, BOOL isNullable, BOOL isAutoIncrement)
{
	_name = name;
	_dataType = dataType;
	_length = length;
	_precision = precision;
	_scale = scale;
	_isNullable = isNullable;
	_isAutoIncrement = isAutoIncrement;
}


/////////////////////////////////////////////////////////////////////////////
// CInformixCursor - SQLCursor implementation for Informix

CInformixCursor::CInformixCursor (CInformixCommand *command): _command(0)
{
	(_command = command)->AddRef();
	setResourceDefaults();
	_errorRegister = getConnection();
	getConnection()->attachDatabaseChangeObserver(this);
	open();
}

CInformixCursor::~CInformixCursor ()
{
	if (_command == 0)
		return;

	if (isOpened())
		close();

	getConnection()->detachDatabaseChangeObserver(this);
	_command->Release();
}

CInformixConnection * CInformixCursor::getConnection ()
{
	return _command->getConnection();
}

CCallback * CInformixCursor::getCallback ()
{
	return getConnection()->getCallback();
}

int2 CInformixCursor::getColumnCount ()
{
	return _sqlda->sqld;
}

CInformixVariable& CInformixCursor::getColumn (int2 number)
{
	return _variables[number];
}

CColumnDescription& CInformixCursor::getDescription (int2 number)
{
	return _descriptions[number];
}

bool CInformixCursor::isOpened ()
{
	return _handle != 0;
}

void CInformixCursor::setErrorMessage (const char* errorMessage)
{
	_command->setErrorMessage(errorMessage);
	CErrorHandlingImpl< CUnknownImpl<SQLCursor> >::setErrorMessage(errorMessage);
}

// implementation of SQLCursor interface

SQL_METHOD_IMP CInformixCursor::setOption (eSQLCursorOption eCurOption, INT32 lValue)
{
	return DBXERR_NOTSUPPORTED;
}

SQL_METHOD_IMP CInformixCursor::getOption (eSQLCursorOption eCurOption, pINT32 plValue, INT16 iMaxLength, pINT16 piLength)
{
	return DBXERR_NOTSUPPORTED;
}

SQL_METHOD_IMP CInformixCursor::getColumnCount (pUINT16 puColumns)
{
	*puColumns = getColumnCount();
	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixCursor::getColumnNameLength (UINT16 uColumnNumber, pUINT16 puLen)
{
	*puLen = getDescription(uColumnNumber).getNameLength();
	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixCursor::getColumnName (UINT16 uColumnNumber, pCHAR pColumnName)
{
	strcpy(pColumnName, getDescription(uColumnNumber).getName());
	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixCursor::getColumnType (UINT16 uColumnNumber, pUINT16 puLogType, pUINT16 puSubType)
{
	CColumnDescription& description = getDescription(uColumnNumber);
	*puLogType = description.getDataType().logType;
	*puSubType = description.getDataType().subType;
	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixCursor::getColumnLength (UINT16 uColumnNumber, pUINT32 pulLength)
{
	*pulLength = getDescription(uColumnNumber).getLength();
	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixCursor::getColumnPrecision (UINT16 uColumnNumber, pINT16 piPrecision)
{
	*piPrecision = getDescription(uColumnNumber).getPrecision();
	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixCursor::getColumnScale (UINT16 uColumnNumber, pINT16 piScale)
{
	*piScale = getDescription(uColumnNumber).getScale();
	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixCursor::isNullable (UINT16 uColumnNumber, pBOOL pbNullable)
{
	*pbNullable = getDescription(uColumnNumber).isNullable();
	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixCursor::isAutoIncrement (UINT16 uColumnNumber, pBOOL pbAutoIncrement)
{
	*pbAutoIncrement = getDescription(uColumnNumber).isAutoIncrement();
	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixCursor::isReadOnly (UINT16 uColumnNumber, pBOOL pbReadOnly)
{
	*pbReadOnly = getDescription(uColumnNumber).isReadOnly();
	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixCursor::isSearchable (UINT16 uColumnNumber, pBOOL pbSearchable)
{
	*pbSearchable = getDescription(uColumnNumber).isSearchable();
	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixCursor::isBlobSizeExact (UINT16 uColumnNumber, pBOOL pbBlobExactSize)
{
	*pbBlobExactSize = getDescription(uColumnNumber).isBlobSizeExact();
	return SQL_SUCCESS;
}

static _FetchSpec SINGLE_ROW_FETCH_SPECIFICATION = { 0, 1, 0 };

SQL_METHOD_IMP CInformixCursor::next () SAFE_SQL_METHOD
{
	TRACE_MESSAGE(getCallback())(traceSTMT, "fetch");
	CInformixActivityPtr activity = getConnection()->activate(false, true, true);
	mint sqlCode = sqli_curs_fetch(ESQLINTVERSION, _handle, 0, _sqlda, 0, &SINGLE_ROW_FETCH_SPECIFICATION);
	if (sqlCode == SQLNOTFOUND) {
		close();
		setResourceDefaults();
		return DBXERR_EOF;
	}
	checkInformixResult(sqlCode);
	notify();
	RETURN_SQL_RESULT;
}

SQL_METHOD_IMP CInformixCursor::getString (UINT16 uColumnNumber, pCHAR pData, pBOOL pbIsNull) SAFE_SQL_METHOD
{
	TRACE_MESSAGE(getCallback())(traceDATAOUT, "getString from %s", getDescription(uColumnNumber).getName());
	CInformixVariable& variable = getColumn(uColumnNumber);
	if (!(*pbIsNull = variable.isNull()))
		variable.getValue()->asString(pData);
	RETURN_SQL_RESULT;
}

SQL_METHOD_IMP CInformixCursor::getShort (UINT16 uColumnNumber, pINT16 pData, pBOOL pbIsNull)
{
	TRACE_MESSAGE(getCallback())(traceDATAOUT, "getShort from %s", getDescription(uColumnNumber).getName());
	CInformixVariable& variable = getColumn(uColumnNumber);
	if (!(*pbIsNull = variable.isNull()))
		*pData = *reinterpret_cast<CInformixAbstractSimpleValue<int2> *>(variable.getValue());
	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixCursor::getLong (UINT16 uColumnNumber, pINT32 pData, pBOOL pbIsNull)
{
	TRACE_MESSAGE(getCallback())(traceDATAOUT, "getLong from %s", getDescription(uColumnNumber).getName());
	CInformixVariable& variable = getColumn(uColumnNumber);
	if (!(*pbIsNull = variable.isNull()))
		*pData = *reinterpret_cast<CInformixAbstractSimpleValue<int4> *>(variable.getValue());
	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixCursor::getDouble (UINT16 uColumnNumber, pDFLOAT pData, pBOOL pbIsNull) SAFE_SQL_METHOD
{
	TRACE_MESSAGE(getCallback())(traceDATAOUT, "getDouble from %s", getDescription(uColumnNumber).getName());
	CInformixVariable& variable = getColumn(uColumnNumber);
	if (!(*pbIsNull = variable.isNull()))
		*pData = variable.getValue()->asDouble();
	RETURN_SQL_RESULT;
}

SQL_METHOD_IMP CInformixCursor::getBcd (UINT16 uColumnNumber, pFMTBcd pData, pBOOL pbIsNull) SAFE_SQL_METHOD
{
	TRACE_MESSAGE(getCallback())(traceDATAOUT, "getBcd from %s", getDescription(uColumnNumber).getName());
	CInformixVariable& variable = getColumn(uColumnNumber);
	if (!(*pbIsNull = variable.isNull()))
		*pData = variable.getValue()->asFMTBcd();
	RETURN_SQL_RESULT;
}

SQL_METHOD_IMP CInformixCursor::getTimeStamp (UINT16 uColumnNumber, pCTIMESTAMP pData, pBOOL pbIsNull)
{
	TRACE_MESSAGE(getCallback())(traceDATAOUT, "getTimeStamp from %s", getDescription(uColumnNumber).getName());
	CInformixVariable& variable = getColumn(uColumnNumber);
	if (!(*pbIsNull = variable.isNull()))
		*pData = *reinterpret_cast<CInformixDateTimeValue *>(variable.getValue());
	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixCursor::getTime (UINT16 uColumnNumber, pINT32 pData, pBOOL pbIsNull)
{
	TRACE_MESSAGE(getCallback())(traceDATAOUT, "getTime from %s", getDescription(uColumnNumber).getName());
	return DBXERR_NOTSUPPORTED;
}

SQL_METHOD_IMP CInformixCursor::getDate (UINT16 uColumnNumber, pINT32 pData, pBOOL pbIsNull)
{
	TRACE_MESSAGE(getCallback())(traceDATAOUT, "getDate from %s", getDescription(uColumnNumber).getName());
	CInformixVariable& variable = getColumn(uColumnNumber);
	if (!(*pbIsNull = variable.isNull()))
		*pData = *reinterpret_cast<CInformixDateValue *>(variable.getValue());
	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixCursor::getBytes (UINT16 uColumnNumber, pBYTE pData, pBOOL pbIsNull)
{
	TRACE_MESSAGE(getCallback())(traceDATAOUT, "getBytes from %s", getDescription(uColumnNumber).getName());
	return DBXERR_NOTSUPPORTED;
}

SQL_METHOD_IMP CInformixCursor::getBlobSize (UINT16 uColumnNumber, pUINT32 pulLength, pBOOL pbIsNull) SAFE_SQL_METHOD
{
	TRACE_MESSAGE(getCallback())(traceBLOB, "getBlobSize from %s", getDescription(uColumnNumber).getName());
	CInformixVariable& variable = getColumn(uColumnNumber);
	if (!(*pbIsNull = variable.isNull()))
		*pulLength = reinterpret_cast<CInformixAbstractLOValue *>(variable.getValue())->getBlobSize();
	RETURN_SQL_RESULT;
}

SQL_METHOD_IMP CInformixCursor::getBlob (UINT16 uColumnNumber, pVOID pData, pBOOL pbIsNull, UINT32 ulLength) SAFE_SQL_METHOD
{
	TRACE_MESSAGE(getCallback())(traceBLOB, "getBlob from %s", getDescription(uColumnNumber).getName());
	CInformixVariable& variable = getColumn(uColumnNumber);
	if (!(*pbIsNull = variable.isNull()))
		reinterpret_cast<CInformixAbstractLOValue *>(variable.getValue())->getBlobData(ulLength, pData);
	RETURN_SQL_RESULT;
}

// private methods

void CInformixCursor::setResourceDefaults ()
{
	_handle = 0;
	_sqlda = 0;
	_variables = 0;
	_descriptions = 0;
	_fetchObservers = 0;
}

void CInformixCursor::open ()
{
	ifx_cursor_t *cursorHandle = _command->getHandle()->_SQCcursptr;
	checkInformixResult(sqli_curs_open(ESQLINTVERSION, cursorHandle, &_command->getParameters(), 0, 0, 0, 0));
	_handle = cursorHandle;
	initializeDataArea();
}

void CInformixCursor::initializeDataArea ()
{
	checkInformixResult(sqli_describe_stmt(ESQLINTVERSION, _command->getHandle(), &_sqlda, 0));
	initializeColumns();
}

void CInformixCursor::initializeColumns ()
{
	(_variables = new CInformixVariable [getColumnCount()])--;
	(_descriptions = new CColumnDescription [getColumnCount()])--;

	CInformixColumnFactory::DescribeArgs describeArgs;
	describeArgs.mapping = getConnection()->getTypesMapping();

	CInformixColumnFactory::CreateValueArgs createValueArgs;
	if ((createValueArgs.blobSize = _command->getBlobSize()) > 0)
		createValueArgs.blobSize *= 1024;
	createValueArgs.fetchSubject = this;

	ifx_sqlvar_t *sqlvar = _sqlda->sqlvar;
	CInformixVariable *variable = _variables;
	CColumnDescription *description = _descriptions;
	for (int i = 0; i < getColumnCount(); i++)
	{
		(++variable)->initialize(sqlvar++);
		describeArgs.type = variable->getType();
		describeArgs.length = variable->getLength();
		describeArgs.name = variable->getName();
		describeArgs.xid = variable->getXID();
		describeArgs.typeName = variable->getTypeName();
		describeArgs.description = ++description;
		CInformixColumnFactory::describe(describeArgs);
		createValueArgs.length = describeArgs.length;
		variable->setValue(describeArgs.createValue(createValueArgs));
	}
}

void CInformixCursor::close ()
{
	ENTER_SAFE_CODE;
	freeResources();
	CInformixActivityPtr activity = getConnection()->activate(true, false);
	checkInformixResult(sqli_curs_close(ESQLINTVERSION, _handle));
	LEAVE_SAFE_CODE;
}

void CInformixCursor::freeResources ()
{
	freeSQLDA(_sqlda);
	if (_variables != 0)
		delete [] ++_variables;
	if (_descriptions != 0)
		delete [] ++_descriptions;
	if (_fetchObservers != 0)
		delete _fetchObservers;
}

// implementation of IFetchSubject interface

void CInformixCursor::attach (Observer observer)
{
	if (_fetchObservers == 0)
		_fetchObservers = new FetchObservers;
	_fetchObservers->push_back(observer);
}

void CInformixCursor::notify ()
{
	if (_fetchObservers != 0)
		for (FetchObservers::iterator it = _fetchObservers->begin(); it != _fetchObservers->end(); ++it)
			(*it)->update();
}

// implementation of IDatabaseChangeObserver interface

void CInformixCursor::databaseChanged ()
{
	freeResources();
	setResourceDefaults();
}


/////////////////////////////////////////////////////////////////////////////
// CInformixTrimingCursor
	
SQL_METHOD_IMP CInformixTrimingCursor::getString (UINT16 uColumnNumber, pCHAR pData, pBOOL pbIsNull) SAFE_SQL_METHOD
{
	CInformixVariable& variable = getColumn(uColumnNumber);
	if (!(*pbIsNull = variable.isNull())) {
		variable.getValue()->asString(pData);
		_cstr_t::trim(pData);
	}
	RETURN_SQL_RESULT;
}


/////////////////////////////////////////////////////////////////////////////
// CInformixColumnFactory
	
CInformixColumnFactory::DescribeFunc CInformixColumnFactory::DescribeFuncTable[] =
{
	/* SQLCHAR */			CInformixColumnFactory::describeChar,
	/* SQLSMINT */			CInformixColumnFactory::describeSmallInt,
	/* SQLINT */			CInformixColumnFactory::describeInteger,
	/* SQLFLOAT */			CInformixColumnFactory::describeFloat,
	/* SQLSMFLOAT */		CInformixColumnFactory::describeSmallFloat,
	/* SQLDECIMAL */		CInformixColumnFactory::describeDecimal,
	/* SQLSERIAL */			CInformixColumnFactory::describeSerial,
	/* SQLDATE */			CInformixColumnFactory::describeDate,
	/* SQLMONEY */			CInformixColumnFactory::describeMoney,
	/* SQLNULL */			CInformixColumnFactory::describeUnknown,
	/* SQLDTIME */			CInformixColumnFactory::describeDateTime,
	/* SQLBYTES */			CInformixColumnFactory::describeBytes,
	/* SQLTEXT */			CInformixColumnFactory::describeText,
	/* SQLVCHAR */			CInformixColumnFactory::describeVarChar,
	/* SQLINTERVAL */		CInformixColumnFactory::describeInterval,
	/* SQLNCHAR */			CInformixColumnFactory::describeChar,
	/* SQLNVCHAR */			CInformixColumnFactory::describeVarChar,
	/* SQLINT8 */			CInformixColumnFactory::describeInt8,
	/* SQLSERIAL8 */		CInformixColumnFactory::describeSerial8,
	/* SQLSET */			CInformixColumnFactory::describeLVarCharPtr,
	/* SQLMULTISET */		CInformixColumnFactory::describeLVarCharPtr,
	/* SQLLIST */			CInformixColumnFactory::describeLVarCharPtr,
	/* SQLROW */			CInformixColumnFactory::describeLVarCharPtr,
	/* SQLCOLLECTION */		CInformixColumnFactory::describeLVarCharPtr,
	/* SQLROWREF */			CInformixColumnFactory::describeLVarCharPtr,
	/* reserved */			CInformixColumnFactory::describeUnknown,
	/* reserved */			CInformixColumnFactory::describeUnknown,
	/* reserved */			CInformixColumnFactory::describeUnknown,
	/* reserved */			CInformixColumnFactory::describeUnknown,
	/* reserved */			CInformixColumnFactory::describeUnknown,
	/* reserved */			CInformixColumnFactory::describeUnknown,
	/* reserved */			CInformixColumnFactory::describeUnknown,
	/* reserved */			CInformixColumnFactory::describeUnknown,
	/* reserved */			CInformixColumnFactory::describeUnknown,
	/* reserved */			CInformixColumnFactory::describeUnknown,
	/* reserved */			CInformixColumnFactory::describeUnknown,
	/* reserved */			CInformixColumnFactory::describeUnknown,
	/* reserved */			CInformixColumnFactory::describeUnknown,
	/* reserved */			CInformixColumnFactory::describeUnknown,
	/* reserved */			CInformixColumnFactory::describeUnknown,
	/* SQLUDTVAR */			CInformixColumnFactory::describeVarBinary,
	/* SQLUDTFIXED */		CInformixColumnFactory::describeUDTFixed,
	/* SQLREFSER8 */		CInformixColumnFactory::describeInt8,
	/* SQLLVARCHAR */		CInformixColumnFactory::describeLVarChar,
	/* SQLSENDRECV */		CInformixColumnFactory::describeUnknown,
	/* SQLBOOL */			CInformixColumnFactory::describeBool,
	/* SQLIMPEXP */			CInformixColumnFactory::describeUnknown,
	/* SQLIMPEXPBIN */		CInformixColumnFactory::describeUnknown,
	/* SQLUDRDEFAULT */		CInformixColumnFactory::describeUnknown
};

bool CInformixColumnFactory::DescribeArgs::isNullable () const
{
	return (type & SQLNONULL) == 0;
}

CInformixAbstractValue * CInformixColumnFactory::DescribeArgs::createValue (
	const CInformixColumnFactory::CreateValueArgs& args) const
{
	if (createValueFunc != 0)
		return createValueFunc(args);
	char buffer[1024];
	sprintf(buffer, "Column '%s' is of unsupported data type '%s'", name, typeName);
	throw CInformixDriverError(buffer);
}

void CInformixColumnFactory::describe (CInformixColumnFactory::DescribeArgs& args)
{
	int2 sqltype = args.type & SQLTYPE;

	static const int DESCRIBE_TABLE_SIZE = sizeof(DescribeFuncTable) / sizeof(DescribeFunc);

	if (sqltype < DESCRIBE_TABLE_SIZE)
		DescribeFuncTable[sqltype](args);
	else
		describeUnknown(args);
}

void CInformixColumnFactory::decodeDecimalLength(int4& length, INT16& precision, INT16& scale)
{
	scale = static_cast<INT16>(length & 0xff);
	if (scale != 0xff)
		precision = static_cast<INT16>(length >> 8);
	else
	{
		length = FLOATING_DECIMAL_PRECISION << 8 | FLOATING_DECIMAL_SCALE;
		precision = FLOATING_DECIMAL_PRECISION;
		scale = FLOATING_DECIMAL_SCALE;
	}
}

void CInformixColumnFactory::describeUnknown (CInformixColumnFactory::DescribeArgs& args)
{
	args.description->describe(args.name, DataType(fldUNKNOWN, 0), args.length, 0, 0, args.isNullable(), 0);
	args.createValueFunc = 0;
}
	
void CInformixColumnFactory::describeChar (CInformixColumnFactory::DescribeArgs& args)
{
	args.description->describe(args.name, DataType(fldZSTRING, fldstFIXED), args.length, static_cast<INT16>(args.length), 0, args.isNullable(), 0);
	args.createValueFunc = createCharValue;
}

CInformixAbstractValue * CInformixColumnFactory::createCharValue (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixStringValue<SQLCHAR>(args.length + 1);
}

void CInformixColumnFactory::describeSmallInt (CInformixColumnFactory::DescribeArgs& args)
{
	args.description->describe(args.name, DataType(fldINT16, 0), sizeof(int2), 0, 0, args.isNullable(), 0);
	args.createValueFunc = createSmallIntValue;
}

CInformixAbstractValue * CInformixColumnFactory::createSmallIntValue (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixSimpleValue<int2, SQLSMINT>();
}

void CInformixColumnFactory::describeInteger (CInformixColumnFactory::DescribeArgs& args)
{
	args.description->describe(args.name, DataType(fldINT32, 0), sizeof(int4), 0, 0, args.isNullable(), 0);
	args.createValueFunc = createIntegerValue;
}

CInformixAbstractValue * CInformixColumnFactory::createIntegerValue (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixSimpleValue<int4, SQLINT>();
}

void CInformixColumnFactory::describeFloat (CInformixColumnFactory::DescribeArgs& args)
{
	args.description->describe(args.name, DataType(fldFLOAT, 0), sizeof(double), 0, 0, args.isNullable(), 0);
	args.createValueFunc = createFloatValue;
}

CInformixAbstractValue * CInformixColumnFactory::createFloatValue (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixSimpleValue<double, SQLFLOAT>();
}

void CInformixColumnFactory::describeSmallFloat (CInformixColumnFactory::DescribeArgs& args)
{
	args.description->describe(args.name, DataType(fldFLOAT, 0), sizeof(float), 0, 0, args.isNullable(), 0);
	args.createValueFunc = createSmallFloatValue;
}

CInformixAbstractValue * CInformixColumnFactory::createSmallFloatValue (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixSimpleValue<float, SQLSMFLOAT>();
}

void CInformixColumnFactory::describeDecimal (CInformixColumnFactory::DescribeArgs& args)
{
	INT16 precision, scale;
	decodeDecimalLength(args.length, precision, scale);
	args.description->describe(args.name, args.mapping->decimal, sizeof(decimal), precision, scale, args.isNullable(), 0);
	args.createValueFunc = createDecimalValue;
}

CInformixAbstractValue * CInformixColumnFactory::createDecimalValue (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixDecimalValue<SQLDECIMAL>(args.length, args.fetchSubject);
}

void CInformixColumnFactory::describeSerial (CInformixColumnFactory::DescribeArgs& args)
{
	args.description->describe(args.name, DataType(fldINT32, fldstAUTOINC), sizeof(int4), 0, 0, args.isNullable(), -1);
	args.createValueFunc = createSerialValue;
}

CInformixAbstractValue * CInformixColumnFactory::createSerialValue (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixSimpleValue<int4, SQLSERIAL>();
}

void CInformixColumnFactory::describeDate (CInformixColumnFactory::DescribeArgs& args)
{
	args.description->describe(args.name, DataType(fldDATE, 0), sizeof(int4), 0, 0, args.isNullable(), 0);
	args.createValueFunc = createDateValue;
}

CInformixAbstractValue * CInformixColumnFactory::createDateValue (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixDateValue();
}

void CInformixColumnFactory::describeMoney (CInformixColumnFactory::DescribeArgs& args)
{
	INT16 precision, scale;
	decodeDecimalLength(args.length, precision, scale);
	args.description->describe(args.name, args.mapping->money, sizeof(decimal), precision, scale, args.isNullable(), 0);
	args.createValueFunc = createMoneyValue;
}

CInformixAbstractValue * CInformixColumnFactory::createMoneyValue (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixDecimalValue<SQLMONEY>(args.length, args.fetchSubject);
}

void CInformixColumnFactory::describeDateTime (CInformixColumnFactory::DescribeArgs& args)
{
	args.description->describe(args.name, DataType(fldDATETIME, 0), sizeof(dtime), 0, 0, args.isNullable(), 0);
	args.createValueFunc = createDateTimeValue;
}

CInformixAbstractValue * CInformixColumnFactory::createDateTimeValue (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixDateTimeValue(static_cast<int2>(args.length), args.fetchSubject);
}

void CInformixColumnFactory::describeBytes (CInformixColumnFactory::DescribeArgs& args)
{
	args.description->describe(args.name, DataType(fldBLOB, fldstBINARY), sizeof(loc_t), 0, 0, args.isNullable(), 0);
	args.createValueFunc = createBytesValue;
}

CInformixAbstractValue * CInformixColumnFactory::createBytesValue (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixSimpleLOValue<SQLBYTES>(args.blobSize);
}

void CInformixColumnFactory::describeText (CInformixColumnFactory::DescribeArgs& args)
{
	args.description->describe(args.name, DataType(fldBLOB, fldstMEMO), sizeof(loc_t), 0, 0, args.isNullable(), 0);
	args.createValueFunc = createTextValue;
}

CInformixAbstractValue * CInformixColumnFactory::createTextValue (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixSimpleLOValue<SQLTEXT>(args.blobSize);
}

void CInformixColumnFactory::describeVarChar (CInformixColumnFactory::DescribeArgs& args)
{
	args.description->describe(args.name, DataType(fldZSTRING, 0), args.length, static_cast<INT16>(args.length), 0, args.isNullable(), 0);
	args.createValueFunc = createVarCharValue;
}

CInformixAbstractValue * CInformixColumnFactory::createVarCharValue (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixStringValue<SQLVCHAR>(args.length + 1);
}

void CInformixColumnFactory::describeInterval (CInformixColumnFactory::DescribeArgs& args)
{
	args.description->describe(args.name, DataType(fldZSTRING, 0), INTERVAL_AS_VARCHAR_LENGTH, INTERVAL_AS_VARCHAR_LENGTH, 0, args.isNullable(), 0);
	args.createValueFunc = createIntervalValue;
}

CInformixAbstractValue * CInformixColumnFactory::createIntervalValue (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixIntervalValue(static_cast<int2>(args.length), args.fetchSubject);
}

void CInformixColumnFactory::describeInt8 (CInformixColumnFactory::DescribeArgs& args)
{
	args.description->describe(args.name, args.mapping->int8, sizeof(decimal), INT8_AS_DECIMAL_PRECISION, 0, args.isNullable(), 0);
	args.createValueFunc = createInt8Value;
}

CInformixAbstractValue * CInformixColumnFactory::createInt8Value (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixInt8Value<SQLINT8>(args.fetchSubject);
}

void CInformixColumnFactory::describeSerial8 (CInformixColumnFactory::DescribeArgs& args)
{
	args.description->describe(args.name, args.mapping->int8, sizeof(decimal), INT8_AS_DECIMAL_PRECISION, 0, args.isNullable(), -1);
	args.createValueFunc = createSerial8Value;
}

CInformixAbstractValue * CInformixColumnFactory::createSerial8Value (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixInt8Value<SQLSERIAL8>(args.fetchSubject);
}

void CInformixColumnFactory::describeVarBinary (CInformixColumnFactory::DescribeArgs& args)
{
	describeLVarCharPtr(args);
}

void CInformixColumnFactory::describeUDTFixed (CInformixColumnFactory::DescribeArgs& args)
{
	switch (args.xid)
	{
		case XID_BLOB: {
			args.description->describe(args.name, DataType(fldBLOB, fldstHBINARY), sizeof(ifx_lo_t), 0, 0, args.isNullable(), 0);
			args.createValueFunc = createBlobValue; }; break;

		case XID_CLOB: {
			args.description->describe(args.name, DataType(fldBLOB, fldstHMEMO), sizeof(ifx_lo_t), 0, 0, args.isNullable(), 0);
			args.createValueFunc = createClobValue; }; break;

		case XID_BOOLEAN:
			describeBool(args); break;

		case XID_LVARCHAR:
			describeLVarChar(args); break;

		default:
			describeUnknown(args);
	}
}

CInformixAbstractValue * CInformixColumnFactory::createBlobValue (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixBLOBValue(args.fetchSubject);
}

CInformixAbstractValue * CInformixColumnFactory::createClobValue (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixCLOBValue(args.fetchSubject);
}

void CInformixColumnFactory::describeLVarChar (CInformixColumnFactory::DescribeArgs& args)
{
	if (args.mapping->lvarchar.logType == fldBLOB && args.mapping->lvarchar.subType == fldstFMTMEMO)
		describeLVarCharPtr(args);
	else {
		args.description->describe(args.name, DataType(fldZSTRING, 0), args.length, static_cast<INT16>(args.length), 0, args.isNullable(), 0);
		args.createValueFunc = createLVarCharValue;
	}
}

CInformixAbstractValue * CInformixColumnFactory::createLVarCharValue (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixLVarCharValue(args.length + 1);
}

void CInformixColumnFactory::describeLVarCharPtr (CInformixColumnFactory::DescribeArgs& args)
{
	args.description->describe(args.name, DataType(fldBLOB, fldstFMTMEMO), sizeof(void *), 0, 0, args.isNullable(), 0);
	args.createValueFunc = createLVarCharPtrValue;
}

CInformixAbstractValue * CInformixColumnFactory::createLVarCharPtrValue (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixLVarCharPtrValue();
}

void CInformixColumnFactory::describeBool (CInformixColumnFactory::DescribeArgs& args)
{
	args.description->describe(args.name, DataType(fldBOOL, 0), sizeof(bool), 0, 0, args.isNullable(), 0);
	args.createValueFunc = createBoolValue;
}

CInformixAbstractValue * CInformixColumnFactory::createBoolValue (const CInformixColumnFactory::CreateValueArgs& args)
{
	return new CInformixBooleanValue();
}
